/**
 * jQuery Calx 1.1.8
 * author :  Ikhsan Agustian <ikhsan017@gmail.com>
 * credit :  jison parser generator by Zach Carter <https://github.com/zaach/jison>,
 *              numeral.js for number formatting by Adam Drapper <https://github.com/adamwdraper/Numeral-js>
 *           stackoverflow community :D
 *           thanks for the formula_regex http://stackoverflow.com/users/1957251/khanh-to
 * lisence:  MIT
 */

(function($) {
    //ie support for Array.indexOf
    if (typeof Array.indexOf !== "function") {
        Array.prototype.indexOf = function(obj, start) {
            for (var i = (start || 0); i < this.length; i++) {
                if (this[i] == obj) {
                    return i;
                }
            }
            return -1;
        };
    }

    //ie support for String.trim method
    if (typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, '');
        };
    }

    //ie support for getPrototypeOf
    if ( typeof Object.getPrototypeOf !== "function" ) {
      if ( typeof "test".__proto__ === "object" ) {
        Object.getPrototypeOf = function(object){
          return object.__proto__;
        };
      } else {
        Object.getPrototypeOf = function(object){
          // May break if the constructor has been tampered with
          return object.constructor.prototype;
        };
      }
    }


    //utility function
    var utility = {

        /** number formatter, credit of Adam Drapper's Numeral.js */
        formatter: (function() {
            /************************************
         Constants
         ************************************/

            var numeral, VERSION = '1.4.9',
                // internal storage for language config files
                languages = {},
                currentLanguage = 'en',
                zeroFormat = null,
                // check for nodeJS
                hasModule = (typeof module !== 'undefined' && module.exports);


            /************************************
         Constructors
         ************************************/


            // Numeral prototype object


            function Numeral(number) {
                this._n = number;
            }

            /**
             * Implementation of toFixed() that treats floats more like decimals
             *
             * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
             * problems for accounting- and finance-related software.
             */

            function toFixed(value, precision, optionals) {
                var power = Math.pow(10, precision),
                    output;

                // Multiply up by precision, round accurately, then divide and use native toFixed():
                output = (Math.round(value * power) / power).toFixed(precision);

                if (optionals) {
                    var optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
                    output = output.replace(optionalsRegExp, '');
                }

                return output;
            }

            /************************************
         Formatting
         ************************************/

            // determine what type of formatting we need to do


            function formatNumeral(n, format) {
                var output;

                // figure out what kind of format we are dealing with
                if (format.indexOf('$') > -1) { // currency!!!!!
                    output = formatCurrency(n, format);
                } else if (format.indexOf('%') > -1) { // percentage
                    output = formatPercentage(n, format);
                } else if (format.indexOf(':') > -1) { // time
                    output = formatTime(n, format);
                } else { // plain ol' numbers or bytes
                    output = formatNumber(n, format);
                }

                // return string
                return output;
            }

            // revert to number


            function unformatNumeral(n, string) {
                if (string.indexOf(':') > -1) {
                    n._n = unformatTime(string);
                } else {
                    if (string === zeroFormat) {
                        n._n = 0;
                    } else {
                        var stringOriginal = string;
                        if (languages[currentLanguage].delimiters.decimal !== '.') {
                            string = string.replace(/\./g, '').replace(languages[currentLanguage].delimiters.decimal, '.');
                        }

                        // see if abbreviations are there so that we can multiply to the correct number
                        var thousandRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
                            millionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
                            billionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
                            trillionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');

                        // see if bytes are there so that we can multiply to the correct number
                        var prefixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
                            bytesMultiplier = false;

                        for (var power = 0; power <= prefixes.length; power++) {
                            bytesMultiplier = (string.indexOf(prefixes[power]) > -1) ? Math.pow(1024, power + 1) : false;

                            if (bytesMultiplier) {
                                break;
                            }
                        }

                        // do some math to create our number
                        n._n = ((bytesMultiplier) ? bytesMultiplier : 1) * ((stringOriginal.match(thousandRegExp)) ? Math.pow(10, 3) : 1) * ((stringOriginal.match(millionRegExp)) ? Math.pow(10, 6) : 1) * ((stringOriginal.match(billionRegExp)) ? Math.pow(10, 9) : 1) * ((stringOriginal.match(trillionRegExp)) ? Math.pow(10, 12) : 1) * ((string.indexOf('%') > -1) ? 0.01 : 1) * Number(((string.indexOf('(') > -1) ? '-' : '') + string.replace(/[^0-9\.-]+/g, ''));

                        // round if we are talking about bytes
                        n._n = (bytesMultiplier) ? Math.ceil(n._n) : n._n;
                    }
                }
                return n._n;
            }

            function formatCurrency(n, format) {
                var prependSymbol = (format.indexOf('$') <= 1) ? true : false;

                // remove $ for the moment
                var space = '';

                // check for space before or after currency
                if (format.indexOf(' $') > -1) {
                    space = ' ';
                    format = format.replace(' $', '');
                } else if (format.indexOf('$ ') > -1) {
                    space = ' ';
                    format = format.replace('$ ', '');
                } else {
                    format = format.replace('$', '');
                }

                // format the number
                var output = formatNumeral(n, format);

                // position the symbol
                if (prependSymbol) {
                    if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
                        output = output.split('');
                        output.splice(1, 0, languages[currentLanguage].currency.symbol + space);
                        output = output.join('');
                    } else {
                        output = languages[currentLanguage].currency.symbol + space + output;
                    }
                } else {
                    if (output.indexOf(')') > -1) {
                        output = output.split('');
                        output.splice(-1, 0, space + languages[currentLanguage].currency.symbol);
                        output = output.join('');
                    } else {
                        output = output + space + languages[currentLanguage].currency.symbol;
                    }
                }

                return output;
            }

            function formatPercentage(n, format) {
                var space = '';
                // check for space before %
                if (format.indexOf(' %') > -1) {
                    space = ' ';
                    format = format.replace(' %', '');
                } else {
                    format = format.replace('%', '');
                }

                n._n = n._n * 100;
                var output = formatNumeral(n, format);
                if (output.indexOf(')') > -1) {
                    output = output.split('');
                    output.splice(-1, 0, space + '%');
                    output = output.join('');
                } else {
                    output = output + space + '%';
                }
                return output;
            }

            function formatTime(n, format) {
                var hours = Math.floor(n._n / 60 / 60),
                    minutes = Math.floor((n._n - (hours * 60 * 60)) / 60),
                    seconds = Math.round(n._n - (hours * 60 * 60) - (minutes * 60));
                return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
            }

            function unformatTime(string) {
                var timeArray = string.split(':'),
                    seconds = 0;
                // turn hours and minutes into seconds and add them all up
                if (timeArray.length === 3) {
                    // hours
                    seconds = seconds + (Number(timeArray[0]) * 60 * 60);
                    // minutes
                    seconds = seconds + (Number(timeArray[1]) * 60);
                    // seconds
                    seconds = seconds + Number(timeArray[2]);
                } else if (timeArray.lenght === 2) {
                    // minutes
                    seconds = seconds + (Number(timeArray[0]) * 60);
                    // seconds
                    seconds = seconds + Number(timeArray[1]);
                }
                return Number(seconds);
            }

            function formatNumber(n, format) {
                var negP = false,
                    optDec = false,
                    abbr = '',
                    bytes = '',
                    ord = '',
                    abs = Math.abs(n._n);

                // check if number is zero and a custom zero format has been set
                if (n._n === 0 && zeroFormat !== null) {
                    return zeroFormat;
                } else {
                    // see if we should use parentheses for negative number
                    if (format.indexOf('(') > -1) {
                        negP = true;
                        format = format.slice(1, -1);
                    }

                    // see if abbreviation is wanted
                    if (format.indexOf('a') > -1) {
                        // check for space before abbreviation
                        if (format.indexOf(' a') > -1) {
                            abbr = ' ';
                            format = format.replace(' a', '');
                        } else {
                            format = format.replace('a', '');
                        }

                        if (abs >= Math.pow(10, 12)) {
                            // trillion
                            abbr = abbr + languages[currentLanguage].abbreviations.trillion;
                            n._n = n._n / Math.pow(10, 12);
                        } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9)) {
                            // billion
                            abbr = abbr + languages[currentLanguage].abbreviations.billion;
                            n._n = n._n / Math.pow(10, 9);
                        } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6)) {
                            // million
                            abbr = abbr + languages[currentLanguage].abbreviations.million;
                            n._n = n._n / Math.pow(10, 6);
                        } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3)) {
                            // thousand
                            abbr = abbr + languages[currentLanguage].abbreviations.thousand;
                            n._n = n._n / Math.pow(10, 3);
                        }
                    }

                    // see if we are formatting bytes
                    if (format.indexOf('b') > -1) {
                        // check for space before
                        if (format.indexOf(' b') > -1) {
                            bytes = ' ';
                            format = format.replace(' b', '');
                        } else {
                            format = format.replace('b', '');
                        }

                        var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
                            min, max;

                        for (var power = 0; power <= prefixes.length; power++) {
                            min = Math.pow(1024, power);
                            max = Math.pow(1024, power + 1);

                            if (n._n >= min && n._n < max) {
                                bytes = bytes + prefixes[power];
                                if (min > 0) {
                                    n._n = n._n / min;
                                }
                                break;
                            }
                        }
                    }

                    // see if ordinal is wanted
                    if (format.indexOf('o') > -1) {
                        // check for space before
                        if (format.indexOf(' o') > -1) {
                            ord = ' ';
                            format = format.replace(' o', '');
                        } else {
                            format = format.replace('o', '');
                        }

                        ord = ord + languages[currentLanguage].ordinal(n._n);
                    }

                    if (format.indexOf('[.]') > -1) {
                        optDec = true;
                        format = format.replace('[.]', '.');
                    }

                    var w = n._n.toString().split('.')[0],
                        precision = format.split('.')[1],
                        thousands = format.indexOf(','),
                        d = '',
                        neg = false;

                    if (precision) {
                        if (precision.indexOf('[') > -1) {
                            precision = precision.replace(']', '');
                            precision = precision.split('[');
                            d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length);
                        } else {
                            d = toFixed(n._n, precision.length);
                        }

                        w = d.split('.')[0];

                        if (d.split('.')[1].length) {
                            d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
                        } else {
                            d = '';
                        }

                        if (optDec && Number(d.slice(1)) === 0) {
                            d = '';
                        }
                    } else {
                        w = toFixed(n._n, null);
                    }

                    // format number
                    if (w.indexOf('-') > -1) {
                        w = w.slice(1);
                        neg = true;
                    }

                    if (thousands > -1) {
                        w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
                    }

                    if (format.indexOf('.') === 0) {
                        w = '';
                    }

                    return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
                }
            }

            /************************************
         Top Level Functions
         ************************************/

            numeral = function(input) {
                if (numeral.isNumeral(input)) {
                    input = input.value();
                } else if (!Number(input)) {
                    input = 0;
                }

                return new Numeral(Number(input));
            };

            // version number
            numeral.version = VERSION;

            // compare numeral object
            numeral.isNumeral = function(obj) {
                return obj instanceof Numeral;
            };

            // This function will load languages and then set the global language.  If
            // no arguments are passed in, it will simply return the current global
            // language key.
            numeral.language = function(key, values) {
                if (!key) {
                    return currentLanguage;
                }

                if (key && !values) {
                    if (!languages[key]) {
                        throw new Error('Unknown language : ' + key);
                    }
                    currentLanguage = key;
                }

                if (values || !languages[key]) {
                    loadLanguage(key, values);
                }

                return numeral;
            };

            numeral.language('en', {
                delimiters: {
                    thousands: ',',
                    decimal: '.'
                },
                abbreviations: {
                    thousand: 'k',
                    million: 'm',
                    billion: 'b',
                    trillion: 't'
                },
                ordinal: function(number) {
                    var b = number % 10;
                    return (~~(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th';
                },
                currency: {
                    symbol: '$'
                }
            });

            numeral.zeroFormat = function(format) {
                if (typeof(format) === 'string') {
                    zeroFormat = format;
                } else {
                    zeroFormat = null;
                }
            };

            /************************************
         Helpers
         ************************************/

            function loadLanguage(key, values) {
                languages[key] = values;
            }


            /************************************
         Numeral Prototype
         ************************************/


            numeral.fn = Numeral.prototype = {

                clone: function() {
                    return numeral(this);
                },

                format: function(inputString) {
                    return formatNumeral(this, inputString ? inputString : numeral.defaultFormat);
                },

                unformat: function(inputString) {
                    return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat);
                },

                value: function() {
                    return this._n;
                },

                valueOf: function() {
                    return this._n;
                },

                set: function(value) {
                    this._n = Number(value);
                    return this;
                },

                add: function(value) {
                    this._n = this._n + Number(value);
                    return this;
                },

                subtract: function(value) {
                    this._n = this._n - Number(value);
                    return this;
                },

                multiply: function(value) {
                    this._n = this._n * Number(value);
                    return this;
                },

                divide: function(value) {
                    this._n = this._n / Number(value);
                    return this;
                },

                difference: function(value) {
                    var difference = this._n - Number(value);

                    if (difference < 0) {
                        difference = -difference;
                    }

                    return difference;
                }

            };

            /************************************
         Exposing Numeral
         ************************************/

            // CommonJS module is defined
            if (hasModule) {
                module.exports = numeral;
            }

            /*global ender:false */
            if (typeof ender === 'undefined') {
                // here, `this` means `window` in the browser, or `global` on the server
                // add `numeral` as a global object via a string identifier,
                // for Closure Compiler 'advanced' mode
                this['numeral'] = numeral;
            }

            /*global define:false */
            if (typeof define === 'function' && define.amd) {
                define([], function() {
                    return numeral;
                });
            }

            return numeral;
        }).call(this),

        /**
         * parsing formula to be calculated, generated from jison file
         * need better jison file for better parsing since I know nothing about this
         * especially for formula parsing
         **/
        parser: (function() {
            var parser = {
                trace: function trace() {},
                yy: {},
                symbols_: {
                    "error": 2,
                    "expressions": 3,
                    "e": 4,
                    "EOF": 5,
                    "+": 6,
                    "-": 7,
                    "*": 8,
                    "/": 9,
                    ">": 10,
                    "<": 11,
                    "=": 12,
                    "^": 13,
                    "MOD": 14,
                    "(": 15,
                    ")": 16,
                    "ABS": 17,
                    "ROUND": 18,
                    "FLOOR": 19,
                    "CEIL": 20,
                    "SQRT": 21,
                    "IF": 22,
                    ",": 23,
                    "MAX": 24,
                    "MIN": 25,
                    "AVG": 26,
                    "SUM": 27,
                    "NUMBER": 28,
                    "E": 29,
                    "PI": 30,
                    "$accept": 0,
                    "$end": 1
                },
                terminals_: {
                    2: "error",
                    5: "EOF",
                    6: "+",
                    7: "-",
                    8: "*",
                    9: "/",
                    10: ">",
                    11: "<",
                    12: "=",
                    13: "^",
                    14: "MOD",
                    15: "(",
                    16: ")",
                    17: "ABS",
                    18: "ROUND",
                    19: "FLOOR",
                    20: "CEIL",
                    21: "SQRT",
                    22: "IF",
                    23: ",",
                    24: "MAX",
                    25: "MIN",
                    26: "AVG",
                    27: "SUM",
                    28: "NUMBER",
                    29: "E",
                    30: "PI"
                },
                productions_: [0, [3, 2],
                    [4, 3],
                    [4, 3],
                    [4, 3],
                    [4, 3],
                    [4, 3],
                    [4, 3],
                    [4, 4],
                    [4, 4],
                    [4, 3],
                    [4, 4],
                    [4, 3],
                    [4, 3],
                    [4, 3],
                    [4, 4],
                    [4, 4],
                    [4, 4],
                    [4, 4],
                    [4, 4],
                    [4, 8],
                    [4, 6],
                    [4, 6],
                    [4, 6],
                    [4, 6],
                    [4, 2],
                    [4, 1],
                    [4, 1],
                    [4, 1]
                ],
                performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) { /* this == yyval */

                    var $0 = $$.length - 1;
                    switch (yystate) {
                        case 1:
                            return $$[$0 - 1];
                        case 2:
                            this.$ = $$[$0 - 2] + $$[$0];
                            break;
                        case 3:
                            this.$ = $$[$0 - 2] - $$[$0];
                            break;
                        case 4:
                            this.$ = $$[$0 - 2] * $$[$0];
                            break;
                        case 5:
                            this.$ = $$[$0 - 2] / $$[$0];
                            break;
                        case 6:
                            this.$ = $$[$0 - 2] > $$[$0];
                            break;
                        case 7:
                            this.$ = $$[$0 - 2] < $$[$0];
                            break;
                        case 8:
                            this.$ = $$[$0 - 3] >= $$[$0];
                            break;
                        case 9:
                            this.$ = $$[$0 - 3] <= $$[$0];
                            break;
                        case 10:
                            this.$ = $$[$0 - 2] == $$[$0];
                            break;
                        case 11:
                            this.$ = $$[$0 - 3] != $$[$0];
                            break;
                        case 12:
                            this.$ = Math.pow($$[$0 - 2], $$[$0]);
                            break;
                        case 13:
                            this.$ = $$[$0 - 2] % $$[$0];
                            break;
                        case 14:
                            this.$ = $$[$0 - 1];
                            break;
                        case 15:
                            this.$ = Math.abs($$[$0 - 1]);
                            break;
                        case 16:
                            this.$ = Math.round($$[$0 - 1]);
                            break;
                        case 17:
                            this.$ = Math.floor($$[$0 - 1]);
                            break;
                        case 18:
                            this.$ = Math.ceil($$[$0 - 1]);
                            break;
                        case 19:
                            this.$ = Math.sqrt($$[$0 - 1]);
                            break;
                        case 20:
                            this.$ = ($$[$0 - 5]) ? $$[$0 - 3] : $$[$0 - 1];
                            break;
                        case 21:
                            this.$ = formula.max($$[$0 - 3], $$[$0 - 1]);
                            break;
                        case 22:
                            this.$ = formula.min($$[$0 - 3], $$[$0 - 1]);
                            break;
                        case 23:
                            this.$ = formula.avg($$[$0 - 3], $$[$0 - 1]);
                            break;
                        case 24:
                            this.$ = formula.sum($$[$0 - 3], $$[$0 - 1]);
                            break;
                        case 25:
                            this.$ = -$$[$0];
                            break;
                        case 26:
                            this.$ = Number(yytext);
                            break;
                        case 27:
                            this.$ = Math.E;
                            break;
                        case 28:
                            this.$ = Math.PI;
                            break;
                    }
                },
                table: [{
                        3: 1,
                        4: 2,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        1: [3]
                    }, {
                        5: [1, 18],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27]
                    }, {
                        4: 28,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        15: [1, 29]
                    }, {
                        15: [1, 30]
                    }, {
                        15: [1, 31]
                    }, {
                        15: [1, 32]
                    }, {
                        15: [1, 33]
                    }, {
                        15: [1, 34]
                    }, {
                        15: [1, 35]
                    }, {
                        15: [1, 36]
                    }, {
                        15: [1, 37]
                    }, {
                        15: [1, 38]
                    }, {
                        4: 39,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        5: [2, 26],
                        6: [2, 26],
                        7: [2, 26],
                        8: [2, 26],
                        9: [2, 26],
                        10: [2, 26],
                        11: [2, 26],
                        12: [2, 26],
                        13: [2, 26],
                        14: [2, 26],
                        16: [2, 26],
                        23: [2, 26]
                    }, {
                        5: [2, 27],
                        6: [2, 27],
                        7: [2, 27],
                        8: [2, 27],
                        9: [2, 27],
                        10: [2, 27],
                        11: [2, 27],
                        12: [2, 27],
                        13: [2, 27],
                        14: [2, 27],
                        16: [2, 27],
                        23: [2, 27]
                    }, {
                        5: [2, 28],
                        6: [2, 28],
                        7: [2, 28],
                        8: [2, 28],
                        9: [2, 28],
                        10: [2, 28],
                        11: [2, 28],
                        12: [2, 28],
                        13: [2, 28],
                        14: [2, 28],
                        16: [2, 28],
                        23: [2, 28]
                    }, {
                        1: [2, 1]
                    }, {
                        4: 40,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 41,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 42,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 43,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 44,
                        7: [1, 14],
                        12: [1, 45],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 46,
                        7: [1, 14],
                        10: [1, 48],
                        12: [1, 47],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 49,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 50,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 51,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 52]
                    }, {
                        4: 53,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 54,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 55,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 56,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 57,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 58,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 59,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 60,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 61,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 62,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        5: [2, 25],
                        6: [2, 25],
                        7: [2, 25],
                        8: [2, 25],
                        9: [2, 25],
                        10: [2, 25],
                        11: [2, 25],
                        12: [2, 25],
                        13: [2, 25],
                        14: [2, 25],
                        16: [2, 25],
                        23: [2, 25]
                    }, {
                        5: [2, 2],
                        6: [2, 2],
                        7: [2, 2],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 2],
                        11: [2, 2],
                        12: [2, 2],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 2],
                        23: [2, 2]
                    }, {
                        5: [2, 3],
                        6: [2, 3],
                        7: [2, 3],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 3],
                        11: [2, 3],
                        12: [2, 3],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 3],
                        23: [2, 3]
                    }, {
                        5: [2, 4],
                        6: [2, 4],
                        7: [2, 4],
                        8: [2, 4],
                        9: [2, 4],
                        10: [2, 4],
                        11: [2, 4],
                        12: [2, 4],
                        13: [1, 26],
                        14: [2, 4],
                        16: [2, 4],
                        23: [2, 4]
                    }, {
                        5: [2, 5],
                        6: [2, 5],
                        7: [2, 5],
                        8: [2, 5],
                        9: [2, 5],
                        10: [2, 5],
                        11: [2, 5],
                        12: [2, 5],
                        13: [1, 26],
                        14: [2, 5],
                        16: [2, 5],
                        23: [2, 5]
                    }, {
                        5: [2, 6],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 6],
                        11: [2, 6],
                        12: [2, 6],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 6],
                        23: [2, 6]
                    }, {
                        4: 63,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        5: [2, 7],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 7],
                        11: [2, 7],
                        12: [2, 7],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 7],
                        23: [2, 7]
                    }, {
                        4: 64,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 65,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        5: [2, 10],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [2, 10],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 10],
                        23: [2, 10]
                    }, {
                        5: [2, 12],
                        6: [2, 12],
                        7: [2, 12],
                        8: [2, 12],
                        9: [2, 12],
                        10: [2, 12],
                        11: [2, 12],
                        12: [2, 12],
                        13: [2, 12],
                        14: [2, 12],
                        16: [2, 12],
                        23: [2, 12]
                    }, {
                        5: [2, 13],
                        6: [2, 13],
                        7: [2, 13],
                        8: [2, 13],
                        9: [2, 13],
                        10: [2, 13],
                        11: [2, 13],
                        12: [2, 13],
                        13: [1, 26],
                        14: [2, 13],
                        16: [2, 13],
                        23: [2, 13]
                    }, {
                        5: [2, 14],
                        6: [2, 14],
                        7: [2, 14],
                        8: [2, 14],
                        9: [2, 14],
                        10: [2, 14],
                        11: [2, 14],
                        12: [2, 14],
                        13: [2, 14],
                        14: [2, 14],
                        16: [2, 14],
                        23: [2, 14]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 66]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 67]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 68]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 69]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 70]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 71]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 72]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 73]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 74]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 75]
                    }, {
                        5: [2, 8],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 8],
                        11: [2, 8],
                        12: [2, 8],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 8],
                        23: [2, 8]
                    }, {
                        5: [2, 9],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 9],
                        11: [2, 9],
                        12: [2, 9],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 9],
                        23: [2, 9]
                    }, {
                        5: [2, 11],
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [2, 11],
                        11: [2, 11],
                        12: [2, 11],
                        13: [1, 26],
                        14: [1, 27],
                        16: [2, 11],
                        23: [2, 11]
                    }, {
                        5: [2, 15],
                        6: [2, 15],
                        7: [2, 15],
                        8: [2, 15],
                        9: [2, 15],
                        10: [2, 15],
                        11: [2, 15],
                        12: [2, 15],
                        13: [2, 15],
                        14: [2, 15],
                        16: [2, 15],
                        23: [2, 15]
                    }, {
                        5: [2, 16],
                        6: [2, 16],
                        7: [2, 16],
                        8: [2, 16],
                        9: [2, 16],
                        10: [2, 16],
                        11: [2, 16],
                        12: [2, 16],
                        13: [2, 16],
                        14: [2, 16],
                        16: [2, 16],
                        23: [2, 16]
                    }, {
                        5: [2, 17],
                        6: [2, 17],
                        7: [2, 17],
                        8: [2, 17],
                        9: [2, 17],
                        10: [2, 17],
                        11: [2, 17],
                        12: [2, 17],
                        13: [2, 17],
                        14: [2, 17],
                        16: [2, 17],
                        23: [2, 17]
                    }, {
                        5: [2, 18],
                        6: [2, 18],
                        7: [2, 18],
                        8: [2, 18],
                        9: [2, 18],
                        10: [2, 18],
                        11: [2, 18],
                        12: [2, 18],
                        13: [2, 18],
                        14: [2, 18],
                        16: [2, 18],
                        23: [2, 18]
                    }, {
                        5: [2, 19],
                        6: [2, 19],
                        7: [2, 19],
                        8: [2, 19],
                        9: [2, 19],
                        10: [2, 19],
                        11: [2, 19],
                        12: [2, 19],
                        13: [2, 19],
                        14: [2, 19],
                        16: [2, 19],
                        23: [2, 19]
                    }, {
                        4: 76,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 77,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 78,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 79,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        4: 80,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        23: [1, 81]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 82]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 83]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 84]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 85]
                    }, {
                        4: 86,
                        7: [1, 14],
                        15: [1, 3],
                        17: [1, 4],
                        18: [1, 5],
                        19: [1, 6],
                        20: [1, 7],
                        21: [1, 8],
                        22: [1, 9],
                        24: [1, 10],
                        25: [1, 11],
                        26: [1, 12],
                        27: [1, 13],
                        28: [1, 15],
                        29: [1, 16],
                        30: [1, 17]
                    }, {
                        5: [2, 21],
                        6: [2, 21],
                        7: [2, 21],
                        8: [2, 21],
                        9: [2, 21],
                        10: [2, 21],
                        11: [2, 21],
                        12: [2, 21],
                        13: [2, 21],
                        14: [2, 21],
                        16: [2, 21],
                        23: [2, 21]
                    }, {
                        5: [2, 22],
                        6: [2, 22],
                        7: [2, 22],
                        8: [2, 22],
                        9: [2, 22],
                        10: [2, 22],
                        11: [2, 22],
                        12: [2, 22],
                        13: [2, 22],
                        14: [2, 22],
                        16: [2, 22],
                        23: [2, 22]
                    }, {
                        5: [2, 23],
                        6: [2, 23],
                        7: [2, 23],
                        8: [2, 23],
                        9: [2, 23],
                        10: [2, 23],
                        11: [2, 23],
                        12: [2, 23],
                        13: [2, 23],
                        14: [2, 23],
                        16: [2, 23],
                        23: [2, 23]
                    }, {
                        5: [2, 24],
                        6: [2, 24],
                        7: [2, 24],
                        8: [2, 24],
                        9: [2, 24],
                        10: [2, 24],
                        11: [2, 24],
                        12: [2, 24],
                        13: [2, 24],
                        14: [2, 24],
                        16: [2, 24],
                        23: [2, 24]
                    }, {
                        6: [1, 19],
                        7: [1, 20],
                        8: [1, 21],
                        9: [1, 22],
                        10: [1, 23],
                        11: [1, 24],
                        12: [1, 25],
                        13: [1, 26],
                        14: [1, 27],
                        16: [1, 87]
                    }, {
                        5: [2, 20],
                        6: [2, 20],
                        7: [2, 20],
                        8: [2, 20],
                        9: [2, 20],
                        10: [2, 20],
                        11: [2, 20],
                        12: [2, 20],
                        13: [2, 20],
                        14: [2, 20],
                        16: [2, 20],
                        23: [2, 20]
                    }
                ],
                defaultActions: {
                    18: [2, 1]
                },
                parseError: function parseError(str, hash) {
                    if (hash.recoverable) {
                        this.trace(str);
                    } else {
                        throw new Error(str);
                    }
                },
                parse: function parse(input) {
                    var self = this,
                        stack = [0],
                        vstack = [null],
                        lstack = [],
                        table = this.table,
                        yytext = '',
                        yylineno = 0,
                        yyleng = 0,
                        recovering = 0,
                        TERROR = 2,
                        EOF = 1;
                    this.lexer.setInput(input);
                    this.lexer.yy = this.yy;
                    this.yy.lexer = this.lexer;
                    this.yy.parser = this;
                    if (typeof this.lexer.yylloc == 'undefined') {
                        this.lexer.yylloc = {};
                    }
                    var yyloc = this.lexer.yylloc;
                    lstack.push(yyloc);
                    var ranges = this.lexer.options && this.lexer.options.ranges;
                    if (typeof this.yy.parseError === 'function') {
                        this.parseError = this.yy.parseError;
                    } else {
                        this.parseError = Object.getPrototypeOf(this).parseError;
                    }

                    function popStack(n) {
                        stack.length = stack.length - 2 * n;
                        vstack.length = vstack.length - n;
                        lstack.length = lstack.length - n;
                    }

                    function lex() {
                        var token;
                        token = self.lexer.lex() || EOF;
                        if (typeof token !== 'number') {
                            token = self.symbols_[token] || token;
                        }
                        return token;
                    }
                    var symbol, preErrorSymbol, state, action, a, r, yyval = {},
                        p, len, newState, expected;
                    while (true) {
                        state = stack[stack.length - 1];
                        if (this.defaultActions[state]) {
                            action = this.defaultActions[state];
                        } else {
                            if (symbol === null || typeof symbol == 'undefined') {
                                symbol = lex();
                            }
                            action = table[state] && table[state][symbol];
                        }
                        if (typeof action === 'undefined' || !action.length || !action[0]) {
                            var errStr = '';
                            expected = [];
                            for (p in table[state]) {
                                if (this.terminals_[p] && p > TERROR) {
                                    expected.push('\'' + this.terminals_[p] + '\'');
                                }
                            }
                            if (this.lexer.showPosition) {
                                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
                            } else {
                                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
                            }
                            this.parseError(errStr, {
                                text: this.lexer.match,
                                token: this.terminals_[symbol] || symbol,
                                line: this.lexer.yylineno,
                                loc: yyloc,
                                expected: expected
                            });
                        }
                        if (action[0] instanceof Array && action.length > 1) {
                            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
                        }
                        switch (action[0]) {
                            case 1:
                                stack.push(symbol);
                                vstack.push(this.lexer.yytext);
                                lstack.push(this.lexer.yylloc);
                                stack.push(action[1]);
                                symbol = null;
                                if (!preErrorSymbol) {
                                    yyleng = this.lexer.yyleng;
                                    yytext = this.lexer.yytext;
                                    yylineno = this.lexer.yylineno;
                                    yyloc = this.lexer.yylloc;
                                    if (recovering > 0) {
                                        recovering--;
                                    }
                                } else {
                                    symbol = preErrorSymbol;
                                    preErrorSymbol = null;
                                }
                                break;
                            case 2:
                                len = this.productions_[action[1]][1];
                                yyval.$ = vstack[vstack.length - len];
                                yyval._$ = {
                                    first_line: lstack[lstack.length - (len || 1)].first_line,
                                    last_line: lstack[lstack.length - 1].last_line,
                                    first_column: lstack[lstack.length - (len || 1)].first_column,
                                    last_column: lstack[lstack.length - 1].last_column
                                };
                                if (ranges) {
                                    yyval._$.range = [
                                        lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]
                                    ];
                                }
                                r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
                                if (typeof r !== 'undefined') {
                                    return r;
                                }
                                if (len) {
                                    stack = stack.slice(0, -1 * len * 2);
                                    vstack = vstack.slice(0, -1 * len);
                                    lstack = lstack.slice(0, -1 * len);
                                }
                                stack.push(this.productions_[action[1]][0]);
                                vstack.push(yyval.$);
                                lstack.push(yyval._$);
                                newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
                                stack.push(newState);
                                break;
                            case 3:
                                return true;
                        }
                    }
                    return true;
                }
            }; /* generated by jison-lex 0.2.0 */
            var lexer = (function() {
                var lexer = {

                    EOF: 1,

                    parseError: function parseError(str, hash) {
                        if (this.yy.parser) {
                            this.yy.parser.parseError(str, hash);
                        } else {
                            throw new Error(str);
                        }
                    },

                    // resets the lexer, sets new input
                    setInput: function(input) {
                        this._input = input;
                        this._more = this._backtrack = this.done = false;
                        this.yylineno = this.yyleng = 0;
                        this.yytext = this.matched = this.match = "";
                        this.conditionStack = ["INITIAL"];
                        this.yylloc = {
                            first_line: 1,
                            first_column: 0,
                            last_line: 1,
                            last_column: 0
                        };
                        if (this.options.ranges) {
                            this.yylloc.range = [0, 0];
                        }
                        this.offset = 0;
                        return this;
                    },

                    // consumes and returns one char from the input
                    input: function() {
                        var ch = this._input[0];
                        this.yytext += ch;
                        this.yyleng++;
                        this.offset++;
                        this.match += ch;
                        this.matched += ch;
                        var lines = ch.match(/(?:\r\n?|\n).*/g);
                        if (lines) {
                            this.yylineno++;
                            this.yylloc.last_line++;
                        } else {
                            this.yylloc.last_column++;
                        }
                        if (this.options.ranges) {
                            this.yylloc.range[1]++;
                        }
                        this._input = this._input.slice(1);
                        return ch;
                    },

                    // unshifts one char (or a string) into the input
                    unput: function(ch) {
                        var len = ch.length;
                        var lines = ch.split(/(?:\r\n?|\n)/g);
                        this._input = ch + this._input;
                        this.yytext = this.yytext.substr(0, this.yytext.length - len - 1);
                        this.offset -= len;
                        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
                        this.match = this.match.substr(0, this.match.length - 1);
                        this.matched = this.matched.substr(0, this.matched.length - 1);
                        if (lines.length - 1) {
                            this.yylineno -= lines.length - 1;
                        }
                        var r = this.yylloc.range;
                        this.yylloc = {
                            first_line: this.yylloc.first_line,
                            last_line: this.yylineno + 1,
                            first_column: this.yylloc.first_column,
                            last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
                        };
                        if (this.options.ranges) {
                            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
                        }
                        this.yyleng = this.yytext.length;
                        return this;
                    },

                    // When called from action, caches matched text and appends it on next action
                    more: function() {
                        this._more = true;
                        return this;
                    },

                    // When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
                    reject: function() {
                        if (this.options.backtrack_lexer) {
                            this._backtrack = true;
                        } else {
                            return this.parseError("Lexical error on line " + (this.yylineno + 1) + ". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n" + this.showPosition(), {
                                text: "",
                                token: null,
                                line: this.yylineno
                            });
                        }
                        return this;
                    },

                    // retain first n characters of the match
                    less: function(n) {
                        this.unput(this.match.slice(n));
                    },

                    // displays already matched input, i.e. for error messages
                    pastInput: function() {
                        var past = this.matched.substr(0, this.matched.length - this.match.length);
                        return (past.length > 20 ? "..." : "") + past.substr(-20).replace(/\n/g, "");
                    },

                    // displays upcoming input, i.e. for error messages
                    upcomingInput: function() {
                        var next = this.match;
                        if (next.length < 20) {
                            next += this._input.substr(0, 20 - next.length);
                        }
                        return (next.substr(0, 20) + (next.length > 20 ? "..." : "")).replace(/\n/g, "");
                    },

                    // displays the character position where the lexing error occurred, i.e. for error messages
                    showPosition: function() {
                        var pre = this.pastInput();
                        var c = new Array(pre.length + 1).join("-");
                        return pre + this.upcomingInput() + "\n" + c + "^";
                    },

                    // test the lexed token: return FALSE when not a match, otherwise return token
                    test_match: function(match, indexed_rule) {
                        var token, lines, backup;
                        if (this.options.backtrack_lexer) {
                            backup = {
                                yylineno: this.yylineno,
                                yylloc: {
                                    first_line: this.yylloc.first_line,
                                    last_line: this.last_line,
                                    first_column: this.yylloc.first_column,
                                    last_column: this.yylloc.last_column
                                },
                                yytext: this.yytext,
                                match: this.match,
                                matches: this.matches,
                                matched: this.matched,
                                yyleng: this.yyleng,
                                offset: this.offset,
                                _more: this._more,
                                _input: this._input,
                                yy: this.yy,
                                conditionStack: this.conditionStack.slice(0),
                                done: this.done
                            };
                            if (this.options.ranges) {
                                backup.yylloc.range = this.yylloc.range.slice(0);
                            }
                        }
                        lines = match[0].match(/(?:\r\n?|\n).*/g);
                        if (lines) {
                            this.yylineno += lines.length;
                        }
                        this.yylloc = {
                            first_line: this.yylloc.last_line,
                            last_line: this.yylineno + 1,
                            first_column: this.yylloc.last_column,
                            last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length
                        };
                        this.yytext += match[0];
                        this.match += match[0];
                        this.matches = match;
                        this.yyleng = this.yytext.length;
                        if (this.options.ranges) {
                            this.yylloc.range = [this.offset, this.offset += this.yyleng];
                        }
                        this._more = false;
                        this._backtrack = false;
                        this._input = this._input.slice(match[0].length);
                        this.matched += match[0];
                        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
                        if (this.done && this._input) {
                            this.done = false;
                        }
                        if (token) {
                            if (this.options.backtrack_lexer) {
                                delete backup;
                            }
                            return token;
                        } else if (this._backtrack) {
                            for (var k in backup) {
                                this[k] = backup[k];
                            }
                            return false;
                        }
                        if (this.options.backtrack_lexer) {
                            delete backup;
                        }
                        return false;
                    },

                    // return next match in input
                    next: function() {
                        if (this.done) {
                            return this.EOF;
                        }
                        if (!this._input) {
                            this.done = true;
                        }
                        var token, match, tempMatch, index;
                        if (!this._more) {
                            this.yytext = "";
                            this.match = "";
                        }
                        var rules = this._currentRules();
                        for (var i = 0; i < rules.length; i++) {
                            tempMatch = this._input.match(this.rules[rules[i]]);
                            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
                                match = tempMatch;
                                index = i;
                                if (this.options.backtrack_lexer) {
                                    token = this.test_match(tempMatch, rules[i]);
                                    if (token !== false) {
                                        return token;
                                    } else if (this._backtrack) {
                                        match = false;
                                        continue;
                                    } else {
                                        return false;
                                    }
                                } else if (!this.options.flex) {
                                    break;
                                }
                            }
                        }
                        if (match) {
                            token = this.test_match(match, rules[index]);
                            if (token !== false) {
                                return token;
                            }
                            return false;
                        }
                        if (this._input === "") {
                            return this.EOF;
                        } else {
                            return this.parseError("Lexical error on line " + (this.yylineno + 1) + ". Unrecognized text.\n" + this.showPosition(), {
                                text: "",
                                token: null,
                                line: this.yylineno
                            });
                        }
                    },

                    // return next match that has a token
                    lex: function lex() {
                        var r = this.next();
                        if (r) {
                            return r;
                        } else {
                            return this.lex();
                        }
                    },

                    // activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
                    begin: function begin(condition) {
                        this.conditionStack.push(condition);
                    },

                    // pop the previously active lexer condition state off the condition stack
                    popState: function popState() {
                        var n = this.conditionStack.length - 1;
                        if (n > 0) {
                            return this.conditionStack.pop();
                        } else {
                            return this.conditionStack[0];
                        }
                    },

                    // produce the lexer rule set which is active for the currently active lexer condition state
                    _currentRules: function _currentRules() {
                        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
                            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
                        } else {
                            return this.conditions["INITIAL"].rules;
                        }
                    },

                    // return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
                    topState: function topState(n) {
                        n = this.conditionStack.length - 1 - Math.abs(n || 0);
                        if (n >= 0) {
                            return this.conditionStack[n];
                        } else {
                            return "INITIAL";
                        }
                    },

                    // alias for begin(condition)
                    pushState: function pushState(condition) {
                        this.begin(condition);
                    },

                    // return the number of states currently on the stack
                    stateStackSize: function stateStackSize() {
                        return this.conditionStack.length;
                    },
                    options: {},
                    performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {

                        var YYSTATE = YY_START;
                        switch ($avoiding_name_collisions) {
                            case 0:
                                /* skip whitespace */
                                break;
                            case 1:
                                return 28;
                            case 2:
                                return 8;
                            case 3:
                                return 9;
                            case 4:
                                return 14;
                            case 5:
                                return 7;
                            case 6:
                                return 6;
                            case 7:
                                return 13;
                            case 8:
                                return 21;
                            case 9:
                                return 15;
                            case 10:
                                return 16;
                            case 11:
                                return 23;
                            case 12:
                                return 10;
                            case 13:
                                return 11;
                            case 14:
                                return 12;
                            case 15:
                                return 30;
                            case 16:
                                return 29;
                            case 17:
                                return 22;
                            case 18:
                                return 17;
                            case 19:
                                return 18;
                            case 20:
                                return 19;
                            case 21:
                                return 20;
                            case 22:
                                return 24;
                            case 23:
                                return 25;
                            case 24:
                                return 26;
                            case 25:
                                return 27;
                            case 26:
                                return 5;
                            case 27:
                                return 'INVALID';
                        }
                    },
                    rules: [/^(?:\s+)/, /^(?:[0-9]+(\.[0-9]+)?\b)/, /^(?:\*)/, /^(?:\/)/, /^(?:MOD\b)/, /^(?:-)/, /^(?:\+)/, /^(?:\^)/, /^(?:SQRT\b)/, /^(?:\()/, /^(?:\))/, /^(?:,)/, /^(?:>)/, /^(?:<)/, /^(?:=)/, /^(?:PI\b)/, /^(?:E\b)/, /^(?:IF\b)/, /^(?:ABS\b)/, /^(?:ROUND\b)/, /^(?:FLOOR\b)/, /^(?:CEIL\b)/, /^(?:MAX\b)/, /^(?:MIN\b)/, /^(?:AVG\b)/, /^(?:SUM\b)/, /^(?:$)/, /^(?:.)/],
                    conditions: {
                        "INITIAL": {
                            "rules": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
                            "inclusive": true
                        }
                    }
                };
                return lexer;
            })();
            parser.lexer = lexer;

            function Parser() {
                this.yy = {};
            }
            Parser.prototype = parser;
            parser.Parser = Parser;
            return new Parser();
        })(),

        /** convert string char to number, e.g A => 1, Z => 26, AA => 27 */
        toNum: function(chr) {
            chr = chr.split();
            var base = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"],
                i, j, result = 0;

            for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
                result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
            }
            return result;
        },

        /** convert number to string char, e.g 1 => A, 26 => Z, 27 => AA */
        toChr: function(num) {
            var s = "";
            num = num - 1;
            while (num >= 0) {
                s = String.fromCharCode(num % 26 + 97) + s;
                num = Math.floor(num / 26) - 1;
            }
            return s.toUpperCase();
        },

        /** translate cell into row-col pair for easy range iteration, e.g B20 => {row:20, col:2} , or 20.2 when type='string'*/
        translateCell: function($cell, $type) {
            var num = $cell.match(/\d+$/);
            var alpha = $cell.replace(num, '');

            var $return = {
                row: parseInt(num[0], 10),
                col: utility.toNum(alpha)
            };

            if ($type == 'string') {
                return $return.col + '.' + $return.row;
            } else {
                return $return;
            }
        },

        /** iterating cell range, 1.101 type index, throw $return object to callback*/
        iterateCell: function(a, b, callback) {
            var $return = {
                index: [],
                //list of cell index, A1,A2,A3 and so on
                value: [] //list of cell values
            };
            var $start = ('' + a).split('.');
            var $stop = ('' + b).split('.');

            $start[1] = $start[1].substring(0, $start[1].length - 1);
            $stop[1] = $stop[1].substring(0, $stop[1].length - 1);

            for (var ax=0; ax < $start.length; ax++) {
                $start[ax] = parseInt($start[ax], 10);
                $stop[ax] = parseInt($stop[ax], 10);
            }

            var $c_start = ($start[0] < $stop[0]) ? $start[0] : $stop[0];
            var $c_stop = ($start[0] > $stop[0]) ? $start[0] : $stop[0];
            var $r_start = ($start[1] < $stop[1]) ? $start[1] : $stop[1];
            var $r_stop = ($start[1] > $stop[1]) ? $start[1] : $stop[1];

            for (col = $c_start; col <= $c_stop; col++) {
                for (row = $r_start; row <= $r_stop; row++) {
                    var $cellIndex = utility.toChr(col) + row;
                    var $cellValue = calx.matrix[formula.key].value[$cellIndex];
                    $cellValue = ($cellValue) ? parseFloat($cellValue) : 0;

                    $return.index.push($cellIndex);
                    $return.value.push($cellValue);
                }
            }

            return callback.apply(callback, [$return]);
        }
    };


    //default plugin configuration
    var defaults = {
        //autocalculate value on form change when used on form
        autocalculate: true,

        //autocalculate trigger: not yet implemented
        trigger: 'blur',

        //default format used when no data-format found
        format: '0[.]00',

        //mark input with data-formula attribute as readonly
        readonly: true,

        //default language id
        language: 'en',

        //zero formatting
        zeroformat: null
    };

    var form_element = ['input', 'button', 'select', 'textarea', 'div'];

    //formula function
    var formula = { /** list of registered member that use range as parameter, capitalized */
        member: ['MAX', 'MIN', 'SUM', 'AVG'],
        error: [],
        key: '',

        /** member function declaration, function name must be in lower case
         *  instead of A3 or C10 as parameter, function will recieve 1.3+'1' => 1.31 or 3.10+'1" => 3.101 as parameter (col.row)
         **/
        max: function(a, b) {
            return utility.iterateCell(a, b, function($cell) {
                return Math.max.apply(Math, $cell.value);
            });
        },
        min: function(a, b) {
            return utility.iterateCell(a, b, function($cell) {
                return Math.min.apply(Math, $cell.value);
            });
        },
        sum: function(a, b) {
            return utility.iterateCell(a, b, function($cell) {
                var $result = 0;
                for (var i=0; i < $cell.value.length; i++) {
                    $result += $cell.value[i];
                }
                return $result;
            });
        },
        avg: function(a, b) {
            return utility.iterateCell(a, b, function($cell) {
                var $result = 0;
                for (var i=0; i < $cell.value.length; i++) {
                    $result += $cell.value[i];
                }

                return ($result / $cell.value.length);
            });
        }
    };

    /** planned for future added formula */
    formula.financial = {

    };

    formula.math = {

    };

    formula.statistic = {

    };

    formula.logic = {

    };

    formula.general = {

    };

    /** matrix contain cache of all form element value and format */
    var matrix = function($key, $form) {
    	this.key = $key;
    	this.form = $form; //SJP
        this.lang = 'en';
        this.zeroformat = null;
        this.data = {}; //detail data attribute of each cell
        this.value = {}; //native numeric value of each cell
    };

    /** update matrix value when form data changed */
    matrix.prototype.update = function($apply) {
        var $dataKey;

        if (typeof($apply) == 'undefined') {
            $apply = false;
        }

        /** prepare update status for each cell */
        for ($dataKey in this.data) {
            if (this.data[$dataKey].dependency.length === 0) {
                this.data[$dataKey].updated = true;
            } else {
                this.data[$dataKey].updated = false;
            }
        }

        /** for each element with formula in it, process the formula */
        for ($dataKey in this.data) {
            if (typeof(this.data[$dataKey].formula) != 'undefined') {
                this.calculate($dataKey, $apply);
            }
        }
    };

    /** calculate single matrix data member, including it's dependencies */
    matrix.prototype.calculate = function($key, $apply) {
        /** if cell not updated, calculate it! */
        if (!this.data[$key].updated) {
            if (this.data[$key].dependency.length !== 0) {
                var $dkey;

                for ($dkey=0; $dkey <this.data[$key].dependency.length; $dkey++) {
                    var $dval = this.data[$key].dependency[$dkey];

                    if(typeof(this.data[$dval])=='object'){
                        if (!this.data[$dval].updated) {
                            this.calculate($dval);
                        }
                    }
                }
            }

            //replace the formula with the value
            if (typeof(this.data[$key].formula) != 'undefined') {
                var $replaceVal = {};
                var $stringVal = '';
                var $k;
                for ($k in this.data[$key].dependency) {
                    var $v = this.data[$key].dependency[$k];
                    $replaceVal['$' + $v] = this.value[$v];
                    $stringVal += this.value[$v];
                }

                if (this.data[$key].formula.trim() !== '') {
                    var $equation = '';
                    var $regex = '(' + formula.member.join('|') + ')\\(([^(^)]*)\\)';
                    var $formula_regex = new RegExp($regex, 'g');

                    $equation = this.data[$key].formula.replace($formula_regex, function($range) {
                        $range = $range.replace(/\$\w+/g, function($key) {
                            $key = $key.replace('$', '');
                            $key = utility.translateCell($key, 'string');
                            return $key + '1';
                        });
                        return ($range);
                    });

                    $equation = $equation.replace(/\$\w+/g, function($key) {
                        return $replaceVal[$key] || '0';
                    });

                    //if all value matched, execute the formula
                    if ($equation.indexOf('$') < 0) {
                        formula.key = this.key;
                        var $result = utility.parser.parse($equation);
                        this.data[$key].value = isNaN($result) ? 0 : $result;
                        this.value[$key] = isNaN($result) ? '' : this.data[$key].value;
                    }
                }
            }
        }
        this.data[$key].updated = true;
        if ($apply) {
            this.apply($key);
        }

    };

    matrix.prototype.apply = function($key) {
        utility.formatter.language(this.lang);
        if (typeof($key) == 'undefined') {
            $.each(this.value, function($index, $val) {
                var $key = $index.replace(/\$/g, '');
				var $el = this.form.find('*[data-id="' + $key + '"]'); //SJP

                if($el.length!==0){
                    if (form_element.indexOf($el.prop('tagName').toLowerCase()) > -1) {
                        $el.val(utility.formatter(this.value[$key]).format(this.data[$key].format));
                    } else {
                        $el.html(utility.formatter(this.value[$key]).format(this.data[$key].format));
                    }
                }

            });
        } else {
        	var $el = this.form.find('*[data-id="' + $key + '"]');

            if($el.length!==0){
                if (form_element.indexOf($el.prop('tagName').toLowerCase()) > -1) {
                    $el.val(utility.formatter(this.value[$key]).format(this.data[$key].format));
                } else {
                    $el.html(utility.formatter(this.value[$key]).format(this.data[$key].format));
                }
            }
        }
    };

    matrix.prototype.clean = function(){
        var $this = this;
        $.each($this.value, function (id, val) {
            if(this.form.find('*[data-id="' + id + '"]').length === 0){
                delete $this.data[id];
                delete $this.value[id];
            }
        });
    };


    /** calx function member */
    var calx = {
        /** list of affected elements for each form */
        cell: {},

        /** matrix collection for each form */
        matrix: {},

        /** default settings */
        settings: {},

        /** initialize the plugin */
        init: function($options) {
            return this.each(function() {
                var $form = $(this);
                if($form.attr('data-calx-enable')!='1'){
                	var $key = new Date().valueOf();
                    calx.matrix[$key] = new matrix($key,$form); //SJP
                    calx.cell[$key] = [];
                    calx.settings[$key] = $.extend({}, defaults, $options);

                    $form.attr('data-key', $key);
                    $form.attr('data-calx-enable', 1);
                    var $lang = calx.setLang($key);
                    calx.matrix[$key].lang = $lang;
                    calx.scan($form);
                }else{
                    calx.scan($form);
                }
            });
        },

        /** calculate the matrix and apply */
        update: function($formkey) {
            if (typeof($formkey) == 'undefined') {
                return this.each(function() {
                    var $form = $(this);
                    var $formkey = $form.attr('data-key');
                    calx.matrix[$formkey].update(true);
                });
            } else {
                calx.matrix[$formkey].update(true);
            }
        },

        /** rescan the form and recalculate matrix */
        refresh: function() {
            return this.each(function() {
                var $form = $(this);
                var $formkey = $form.attr('data-key');
                //calx.setLang($formkey);
                calx.scan($form);
                //calx.update($formkey);

            });
        },

        /** 
         * scan the form and build the calculation matrix
         * @param  {[type]} $form [description]
         * @return {[type]}       [description]
         */
        scan: function($form) {
            var $formkey = $form.attr('data-key');

            /** registering onChange event for affected elements */
            var registerEvent = function() {
                var $el = $(this);
                var $tag = $el.prop('tagName').toLowerCase();
                var $id = $el.attr('data-id'); //SJP

                if (form_element.indexOf($tag) > -1) {
                	var $type = $el.attr('type');

                	// *** BEGIN SJP ***//
                	if (typeof $type == 'undefined')
                		$type = $el.attr('data-type');

                	if ($type == 'checkboxlist') {
                		var $cbName = $el.attr('name');
                		$rootDiv = ('[name="' + $cbName + '"]');
                		//$cbName = $cbName.substr(0, $cbName.indexOf('div'));
                		var $checkboxgroup = $el.find('[type="checkbox"]');
                		$checkboxgroup.each(function () {
                			$(this).unbind('change').change(function () {
                				var $rootId = $(this).attr('data-rootid');
                				var $cbName = $(this).attr('data-name');
                				var $checkboxgroup = $('[data-name=' + $cbName + ']');
                				calx.matrix[$formkey].value[$rootId] = 0;
                				$checkboxgroup.each(function () {
                					var $c = $(this);
                					var $cid = $c.attr('id');
                					var $value_checked = $c.attr('value');
                					var $value_unchecked = $c.attr('data-unchecked');
                					$value_unchecked = ($value_unchecked) ? $value_unchecked : 0;
                					var $value = ($c.is(':checked')) ? $value_checked : $value_unchecked;
                					calx.matrix[$formkey].value[$rootId] += $.isNumeric($value) ? parseFloat($value) : $value;
                					calx.matrix[$formkey].update(calx.settings[$formkey].autocalculate);
                				});
                			});
                		});
                	}
                	else if ($type == 'radiobuttonlist') {
                		var $rbName = $el.attr('name');
                		$rootDiv = ('[name="' + $rbName + '"]');
                		var $radiobuttongroup = $el.find('[type="radio"]');
                		$radiobuttongroup.each(function () {
                			$(this).unbind('change').change(function () {
                				var $rootId = $(this).attr('data-rootid');
                				var $rbName = $(this).attr('data-name');
                				var $radiobutton = $('[data-name=' + $rbName + ']:checked');
                				var $rbvalue = $radiobutton.attr('value');
                				calx.matrix[$formkey].value[$rootId] = $.isNumeric($rbvalue) ? parseFloat($rbvalue) : $rbvalue;
                				calx.matrix[$formkey].update(calx.settings[$formkey].autocalculate);
                			});
                		});
                	}
                	// *** END SJP ***//
                	else {
                		$el.unbind('change').change(function () {
                			if ($type == 'checkbox') {
                				var $value_checked = $el.attr('value');
                				var $value_unchecked = $el.attr('data-unchecked');
                				$value_unchecked = ($value_unchecked) ? $value_unchecked : 0;
                				var $value = ($el.is(':checked')) ? $value_checked : $value_unchecked;
                				calx.matrix[$formkey].value[$id] = $.isNumeric($value) ? parseFloat($value) : $value;
                			} else if ($type == 'radio') {
                				var $name = $el.attr('name');
                				var $radiogroup = $('[name=' + $name + ']');

                				$radiogroup.each(function () {
                					var $r = $(this);
                					var $rid = $r.attr('id');
                					var $value_checked = $r.attr('value');
                					var $value_unchecked = $r.attr('data-unchecked');
                					$value_unchecked = ($value_unchecked) ? $value_unchecked : 0;

                					var $value = ($r.is(':checked')) ? $value_checked : $value_unchecked;
                					calx.matrix[$formkey].value[$rid] = $.isNumeric($value) ? parseFloat($value) : $value;
                				});
                			} else {
                				var $value = $el.val();
                				$value = ($value) ? utility.formatter().unformat($value) : $value;
                				if (calx.matrix[$formkey].data[$id].format.indexOf('%') > -1) {
                					calx.matrix[$formkey].value[$id] = parseFloat($value) / 100;
                				} else {
                					calx.matrix[$formkey].value[$id] = $.isNumeric($value) ? parseFloat($value) : $value;
                				}
                			}

                			calx.matrix[$formkey].update(calx.settings[$formkey].autocalculate);
                		});
                	}
                    if ($type == 'text') {
                        $el.unbind('blur,focus,change').focus(function() {

                            if (calx.matrix[$formkey].data[$id].format.indexOf('%') > -1) {
                                var $percent = (calx.matrix[$formkey].value[$id] * 100);
                                var $orivalue = ($percent % 1 > 0) ? $percent.toFixed(2) : $percent.toFixed(0);

                                $el.val($orivalue);
                            } else {
                                $el.val(calx.matrix[$formkey].value[$id]);
                            }

                        }).blur(function() {
                            var $value = $el.val();

                            if(!$.isNumeric($value)){
                                $value = ($value) ? utility.formatter().unformat($value) : $value;
                            }

                            if (calx.matrix[$formkey].data[$id].format.indexOf('%') > -1) {
                                calx.matrix[$formkey].value[$id] = parseFloat($value) / 100;
                            } else {
                                calx.matrix[$formkey].value[$id] = $.isNumeric($value) ? parseFloat($value) : $value;
                            }
                            calx.setLang($formkey);
                            $el.val(utility.formatter(calx.matrix[$formkey].value[$id]).format(calx.matrix[$formkey].data[$id].format));
                        });
                    }
                }
            };

            /** register new found element to the calculation matrix */
            var registerMatrix = function() {
                var $this = $(this);
                var $id = $this.attr('data-id'); //SJP
                var $formula = $this.attr('data-formula');
                var $placeholder = /\$\w+/g;
                var $dependency = [];
                var $value = 0;

                /** scan cell dependency and register it to calculation matrix */
                var registerDependency = function() {
                    var match;
                    while (match = $placeholder.exec($formula)) {
                        var $key = match[0].replace('$', '');
                        if ($dependency.indexOf($key) < 0) {
                            $dependency.push($key);
                        }
                        if (calx.cell[$formkey].indexOf('*[data-id="' + $key + '"]') < 0) { //SJP
                        	calx.cell[$formkey].push('*[data-id="' + $key + '"]'); //SJP
                        }
                    }

                    var $regex = '(' + formula.member.join('|') + ')\\(([^(^)]*)\\)';
                    var $formula_regex = new RegExp($regex, 'g');
                    while (match = $formula_regex.exec($formula)) {
                        var $range = match[2].replace(/\$/g, '').split(',');
                        var $start = utility.translateCell($range[0]);
                        var $stop = utility.translateCell($range[1]);

                        for (col = $start.col; col <= $stop.col; col++) {
                            for (row = $start.row; row <= $stop.row; row++) {
                                var $rowIndex = utility.toChr(col) + row;
                                if ($dependency.indexOf($rowIndex) < 0) {
                                    $dependency.push($rowIndex);
                                }
                                if (calx.cell[$formkey].indexOf('*[data-id="' + $rowIndex + '"]') < 0) { //SJP
                                	calx.cell[$formkey].push('*[data-id="' + $rowIndex + '"]'); //SJP
                                }
                            }
                        }
                    }
                };

                /** if cell is not registered in the matrix, in case of refreshing dynamic form, register it and it's dependencies! */
                if (typeof(calx.matrix[$formkey].data[$id]) == 'undefined') {
                    registerEvent.apply(this);

                    var $format = $this.attr('data-format');
                    $format = (typeof($format) == 'undefined' || $format==='') ? calx.settings[$formkey].format : $format;

                    var $tagname = $this.prop('tagName').toLowerCase();

                    if (form_element.indexOf($tagname) > -1) {
                    	// *** BEGIN SJP ***//
                    	var $type = $this.attr('type') //SJP
                    	if (typeof $type == 'undefined')
                    		$type = $this.attr('data-type');
                    	if (typeof ($type) != 'undefined')
                    		$type = $type.toLowerCase();

                        if ($type == 'checkbox' || $type == 'radio') {
                            var $value_checked = $this.attr('value');
                            var $value_unchecked = $this.attr('data-unchecked');
                            $value_unchecked = ($value_unchecked) ? $value_unchecked : 0;

                            $value = ($this.is(':checked')) ? $value_checked : $value_unchecked;
                        }
                        else if ($type == 'checkboxlist') {
                        	var $cbName = $this.attr('name');
                        	$rootDiv = ('[name="' + $cbName + '"]')
                        	var $checkbox = $this.find('[type="checkbox"]').first();
                        	var $rootId = $checkbox.attr('data-rootid');
                        	var $cbName = $checkbox.attr('data-name');
                        	var $checkboxgroup = $('[data-name=' + $cbName + ']');
                        	calx.matrix[$formkey].value[$rootId] = 0;
                        	$checkboxgroup.each(function () {
                        		var $c = $(this);
                        		var $cid = $c.attr('id');
                        		var $value_checked = $c.attr('value');
                        		var $value_unchecked = $c.attr('data-unchecked');
                        		$value_unchecked = ($value_unchecked) ? $value_unchecked : 0;
                        		var $cbvalue = ($c.is(':checked')) ? $value_checked : $value_unchecked;
                        		$value += $.isNumeric($cbvalue) ? parseFloat($cbvalue) : $cbvalue;
                        	});
                        }
                        else if ($type == 'radiobuttonlist') {
                        	var $radiobutton = $this.find('[type="radio"]:checked');
                        	var $rbvalue = $radiobutton.attr('value');
                        	$value = $.isNumeric($rbvalue) ? parseFloat($rbvalue) : $rbvalue;
                        }
                        // *** END SJP ***//
                        else {
                            $value = $this.val();
                            if ($value !== '' && typeof($value) !== 'undefined') {
                                calx.setLang($formkey);
                                $value = ($.isNumeric($value)) ? $value : utility.formatter().unformat($value);

                                $this.val(utility.formatter($value).format($format));
                            }
                        }
                    } else {
                        $value = $this.text();
                        if ($value !== '' && typeof($value) !== 'undefined') {
                            calx.setLang($formkey);
                            $value = ($.isNumeric($value)) ? $value : utility.formatter().unformat($value);

                            $this.html(utility.formatter($value).format($format));
                        }
                    }

                    /** scan for cell dependency by it's formula */
                    if ($formula) {
                        registerDependency();
                        if (calx.settings[$formkey].readonly) {
                            $this.attr('readonly', true).addClass('readonly');
                        }
                    }
                    /*if(calx.cell[$formkey].indexOf('#'+$id) < 0){
                    calx.cell[$formkey].push('#'+$id);
                    }*/

                    /** cache all info to matrix */
                    calx.matrix[$formkey].data[$id] = {
                        'updated': false,
                        'value': $value,
                        'id': $id,
                        'formula': $formula,
                        'format': $format,
                        'dependency': $dependency
                    };

                    calx.matrix[$formkey].value[$id] = $.isNumeric($value) ? parseFloat($value) : $value;

                    /** or if formula has been changed */
                } else if (calx.matrix[$formkey].data[$id].formula != $formula) {
                    if ($formula) {
                        registerDependency();
                        if (calx.settings[$formkey].readonly) {
                            $this.attr('readonly', true).addClass('readonly');
                        }
                        calx.matrix[$formkey].data[$id].formula = $formula;
                        calx.matrix[$formkey].data[$id].dependency = $dependency;

                    }

                    calx.matrix[$formkey].value[$id] = $.isNumeric($value) ? parseFloat($value) : $value;
                }
            };

            /** register all cells with data-formula attribute to the matrix */


            /** iterate matrix for removing cells which is not present in the dom */
            calx.matrix[$formkey].clean();

            var $resultContainer = $form.find('[data-formula]');
            $resultContainer.each(registerMatrix);

            /** register all cells involved within data-formula to the matrix */
            var $cells = $form.find(calx.cell[$formkey].join(',')); //SJP
            $cells.each(registerMatrix);

            /** register all cells with data-format attribute to the matrix */
            var $formattedCell = $form.find('[data-format]');
            $formattedCell.each(registerMatrix);

            //$cells.each(registerEvent); <-- moved inside registerMatrix
            if (calx.settings[$formkey].autocalculate) {
                calx.update($formkey);
            }
        },

        /** replace the parser with your own parser */
        parser: function(parser) {
            utility.parser = parser;
        },

        setLang: function($formkey) {
            if (typeof(calx.settings[$formkey].language) == 'object') {
                utility.formatter.language(calx.settings[$formkey].language.id);
                return calx.settings[$formkey].language.id;
            } else if (typeof(calx.settings[$formkey].language) == 'string') {
                utility.formatter.language(calx.settings[$formkey].language);
                return calx.settings[$formkey].language;
            }
        },

        /** add language settings */
        language: function($option) {
            if (typeof($option) == 'object') {
                utility.formatter.language($option.id, $option.config);
                if ($option.activate) {
                    utility.formatter.language($option.id);
                }
            } else if (typeof($option) == 'string') {
                utility.formatter.language($option);
            }
        },

        /** detach calx from the form */
        detach: function() {
            return this.each(function() {
                var $form = $(this);
                var $formkey = $form.attr('data-key');

                if ($formkey!=='') {
                    $.each(calx.matrix[$formkey].data, function($k, $v) {
                        $el = $('#' + $k);
                        $el.unbind('blur, focus, change');
                        if ($el.prop('tagName') == 'input') {
                            if ($el.attr('type') == 'text') {
                                if ($v.formula) {
                                    $el.val(calx.matrix[$formkey].value[$el.attr('data-id')]);
                                } else {
                                    $el.val('');
                                }
                            }
                        } else {
                            if ($v.formula) {
                                $el.html('');
                            } else {
                                $el.html(calx.matrix[$formkey].value[$el.attr('data-id')]);
                            }
                        }
                    });
                    delete calx.matrix[$formkey];
                    delete calx.cell[$formkey];
                    delete calx.settings[$formkey];

                    $form.attr('data-key', '');
                    $form.attr('data-calx-enable', '0');
                }
            });
        },

        value : function($cell){
            var $form = $(this);
            var $formkey = $form.attr('data-key');
            if(typeof($cell) != 'undefined'){
                if($formkey && calx.matrix[$formkey].data[$cell]){
                    return calx.matrix[$formkey].value[$cell];
                }else{
                    return false;
                }
            }else{
                return calx.matrix[$formkey].value;
            }
        }
    };

    //method loader
    $.fn.calx = function($action, $options) {
        if (calx[$action]) {
            return calx[$action].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof($action) == 'object' || typeof($action) == 'undefined') {
            return calx.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.calx');
        }
    };

})(jQuery);